﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Enhances edges
    /// If edges are visible but not that strong, 
    /// use this filter to improve the edge visibility
    /// </summary>
    public class EdgeEnhance : BaseImageFilter
    {
        protected byte m_nThreshold = 5;

        /// <summary>
        /// Default constructor
        /// </summary>
        public EdgeEnhance()
        {
        }

        /// <summary>
        /// primary constructor, specify threshold directly
        /// </summary>
        /// <param name="_nThreshold"></param>
        public EdgeEnhance(byte _nThreshold)
        {
            m_nThreshold = _nThreshold;
        }

        /// <summary>
        /// Used by clone to create a deep copy
        /// </summary>
        /// <param name="_edgeEnhance"></param>
        internal EdgeEnhance(EdgeEnhance _edgeEnhance)
        {
            this.m_nThreshold = _edgeEnhance.m_nThreshold;
        }

        /// <summary>
        /// Gets or sets the threshold
        /// </summary>
        public byte Threshold
        {
            get { return m_nThreshold; }
            set { m_nThreshold = value; }
        }

        /// <summary>
        /// edge enhancement filter.
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            // This one works by working out the greatest difference between a nPixel and it's eight neighbours.
            // The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
            UnsafeBitmap b2 = (UnsafeBitmap)bitmap.Clone();

            // GDI+ still lies to us - the return format is BGR, NOT RGB.
            BitmapData bmData = bitmap.BitmapData; //bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmData2 = b2.BitmapData; // b2.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            IntPtr Scan0 = bmData.Scan0;
            IntPtr Scan02 = bmData2.Scan0;

            int nBytesPerPixelCount = bitmap.BytesPerPixelCount;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                byte* p2 = (byte*)(void*)Scan02;

                int nOffset = bitmap.OffsetPointerIncrease; //stride - bitmap.Width * 3;
                int nWidth = bitmap.Width * nBytesPerPixelCount; //bitmap.Width * 3;

                int nPixel = 0;
                int nPixelMax = 0;

                p += stride;
                p2 += stride;

                int nHeight = bitmap.Height;
                int nByteCount = bitmap.BytesPerPixelCount;
                for (int y = 0; y < nHeight - nByteCount; ++y)
                {
                    p += nByteCount; //3;
                    p2 += nByteCount; //3;

                    for (int x = 3; x < nWidth - nByteCount; ++x)
                    {
                        nPixelMax = Math.Abs((p2 - stride + nByteCount)[0] - (p2 + stride - nByteCount)[0]); //nPixelMax = Math.Abs((p2 - stride + 3)[0] - (p2 + stride - 3)[0]);

                        nPixel = Math.Abs((p2 + stride + nByteCount)[0] - (p2 - stride - nByteCount)[0]); //nPixel = Math.Abs((p2 + stride + 3)[0] - (p2 - stride - 3)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs((p2 - stride)[0] - (p2 + stride)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs((p2 + nByteCount)[0] - (p2 - nByteCount)[0]); //nPixel = Math.Abs((p2 + 3)[0] - (p2 - 3)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        if (nPixelMax > m_nThreshold && nPixelMax > p[0])
                            p[0] = (byte)Math.Max(p[0], nPixelMax);

                        ++p;
                        ++p2;
                    }
                    p += nOffset + nByteCount; //nOffset + 3;
                    p2 += nOffset + nByteCount; //nOffset + 3;
                }
            }

            b2.Dispose(); //b2.UnlockBits(bmData2);
            return _bitmap;
        }

        /// <summary>
        /// Used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new EdgeEnhance(this);
        }
    }
}
